#pragma once

#include "log.hpp"
#include "ThreadPool.hpp"
#include "Task.hpp"
#include <string>
#include <cstring>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/wait.h>
#include <unistd.h>
#include <pthread.h>


#define gbacklog 5

using namespace std;

enum
{
    USAGE_ERR = 1,
    SOCK_ERR,
    BIND_ERR,
    LISTEN_ERR,
    READ_ERR
};

class Server;

class ThreadData1
{
public:
    ThreadData1(Server *self, int sock)
        : _self(self), _sock(sock)
    {
    }

public:
    Server *_self;
    int _sock;
};



class Server
{
public:
    Server(const uint16_t &port)
        : _listenSock(-1), _port(port)
    {
    }

    void initServer()
    {
        // std::ofstream ofs("log.txt");    
        // ofs.close();

        // 1.创建套接字
        _listenSock = socket(AF_INET, SOCK_STREAM, 0);
        if (_listenSock < 0)
        {
            logMessage(FATAL, "监听套接字创建失败");
            exit(SOCK_ERR);
        }
        logMessage(NORMAL, "创建套接字成功: %d",_listenSock);

        // 2.绑定端口号
        struct sockaddr_in local;
        memset(&local, 0, sizeof(local));
        local.sin_family = AF_INET;
        local.sin_port = htons(_port);
        local.sin_addr.s_addr = htonl(INADDR_ANY);
        int n = bind(_listenSock, (struct sockaddr *)&local, sizeof(local));
        if (n == -1)
        {
            logMessage(FATAL, "绑定端口号失败");
            exit(BIND_ERR);
        }
        logMessage(NORMAL, "绑定端口号成功");

        // 3.设置监听状态
        n = listen(_listenSock, gbacklog);
        if (n < 0)
        {
            logMessage(FATAL, "监听设置失败");
            exit(LISTEN_ERR);
        }
        logMessage(NORMAL, "监听设置成功");
    }


    void start()
    {
        //线程池初始化
        ThreadPool<Task>* tp=ThreadPool<Task>::getInstance();   //version4
        tp->run();  //run不能放在while循环里，否则每次循环都调用run，每次都创建3个线程 3+3+3+...
        logMessage(NORMAL,"线程池创建成功");

        struct sockaddr_in client;
        socklen_t len = sizeof(client);
        while (1)
        {
            // 多进程可以这样，将SIGCHLD信号的处理动作设置为SIG_IGN，这样子进程结束时会自动清理，不僵尸，也不通知父进程
            //参见进程信号  p16
            // signal(SIGCHLD,SIG_IGN);     //version 2

            // 4.接收连接请求，建立连接
            int sock = accept(_listenSock, (struct sockaddr *)&client, &len);
            if (sock < 0)
            {
                logMessage(ERROR, "接收失败"); // 接首失败 不退出，继续重新接收
                continue;
            }
            logMessage(NORMAL, "链接成功, 新套接字：%d",sock);

            // 5.收发数据，TCP面向字节流，读写数据和文件操作一样
            /********************* version1  accept和收发数据是串行的，即收发数据函数返回后，才会继续接收新的连接请求***/
            // serverIO(sock);
            // close(sock);    //数据收发完毕后关闭文件描述符，否则易造成文件描述符泄露

            /********************version2 多进程，子进程会拷贝父进程的进程地址空间*****************/
            // pid_t id = fork();
            // if (id == 0) // 子进程
            // {
            //     close(_listenSock); // 关闭子进程自己的监听文件描述符，，参见进程间通信 p5

            //     // 主进程的子进程再创建子进程，这里>0的退出，即主进程的子进程退出，我们立马等待成功。
            //     // 那么孙子进程就成为孤儿进程了，由OS领养，孙子进程退出时由OS来回收
            //     // if (fork() > 0)
            //     //     exit(0);
            //     serverIO(sock); // 这里也是用孙子进程自己的sock文件描述符
            //     close(sock);    // 这里也是关闭孙子进程自己的文件描述符。  不关也可以，因为fd随进程
            //     exit(0);    //子进程工作完毕  一定一定一定要退出
            // }

            // //父进程
            // close(sock); // serverIO使用子进程自己的sock，主进程的sock没有被使用，关闭，否则文件描述符泄露
            // // pid_t ret = waitpid(id, nullptr, 0);    //阻塞等待，子进程会立马退出，让孙子进程去执行serverIO
            // // if (ret > 0)
            // // {
            // //     cout << "等待成功" << endl;
            // // }

            /*********************version3 多线程*********************************************/
            // 频繁创建销毁线程
            //  pthread_t tid;
            //  ThreadData1* td=new ThreadData1(this,sock);
            //  pthread_create(&tid,nullptr,routine,td);

            // pthread_join(sock,nullptr);   //线程等待为阻塞式等待，又成为了串行，不可行。采取线程分离，结束自动释放资源

            /*********************version4 线程池************************************************************/
            ThreadPool<Task>* tp=ThreadPool<Task>::getInstance();
            Task t(sock,serverIO);
            tp->push(t);

        }
    }

    static void *routine(void *args)    //version3
    {
        pthread_detach(pthread_self()); // 线程分离，无需join
        ThreadData1 *td = static_cast<ThreadData1 *>(args);
        serverIO(td->_sock);
        close(td->_sock);
        delete td;
        return nullptr;
    }

    ~Server()
    {
    }

private:
    int _listenSock;
    uint16_t _port;
};
